0x0 问题表现
某天实习导师想让我学习一下如何分析Crash Log并抛给了我一个Crash Log,那是我第一次分析Crash Log,Crash Log 是怎么生成的,有哪些重要信息可以帮助开发者解决问题等等关于Crash Log 的介绍就不在这篇文章讲了。(2019.2更新:《Crash分析系列之一:LaunchDaemons中的ReportCrash》在这篇文章里对Crash Log 进行了介绍)
这里直接贴上Crash Log的重要信息:
1 | Exception Type: SIGSEGV |
Crash 发生在Thread 19,通过Thread 19的调用栈看不到问题所在,来看一下Thread State 有什么可利用信息。lr: 0x0000000100688568 ,lr寄存器保存了方法调用完后返回的地址,来验证下这个地址是否在项目工程代码里,0x1000b4000 < 0x100688568 < 0x1029cbfff,lr的内容地址是在项目工程里的,离真相近了一步,再计算一下偏移地址 0x100688568 - 0x1000b4000 = 0x5D4568。拿到了偏移地址,下一节就去定位这个偏移地址看看问题所在。
0x1 分析问题
这里通过使用hopper 看看在0x5D4568 发生了什么
通过hopper定位到 lr 寄存器的内容指向下面这行代码
1 | mov x0, x24 |
所以Crash 就发生在下面这行代码执行之后
1 | bl imp___stubs__objc_msgSend |
再看上面的汇编代码selector是啥,很明显是setHeaderAdModel:
,回到项目工程里看到headerAdModel 是 nonatomic 的,在多线程下进行setter操作便可能对象被重复relaese的问题导致Crash,可以通过runtime的源码看到问题所在:
(2019.02更新)除了用hopper去查看地址对应的符号,还可以用atos命令查看,比如
atos -o /Users/yuewen/Downloads/b83fee03-a9d3-4739-b1dd-71452f08525d/QQReaderUI.app.dSYM/Contents/Resources/DWARF/QQReaderUI -l 0x1000a0000 0x000000010010ee50
FYI
- 一般来说 arm64上 x0 – x7 分别会存放方法的前 8 个参数。
- 一般在[self method]中,x0 存self地址,x1 存selector地址。
- 如果参数个数超过了8个,多余的参数会存在栈上,新方法会通过栈来读取。
- 方法的返回值一般都在 x0 上。
- 如果方法返回值是一个较大的数据结构时,结果会存在 x8 执行的地址上。
0x2 解决问题
最简单的解决方案就是把多线程下进行setter操作的property 设置为 atomic就好了